// 排行榜管理器

import { BaseManager } from '../core/BaseManager';
import { RedisClient } from '../core/RedisClient';
import {
  LeaderboardEntry,
  LeaderboardConfig,
  LeaderboardStats,
  LeaderboardQuery,
  LeaderboardResult,
  TimeWindow,
} from '../types/leaderboard';
import { KeyGenerator } from '../utils/keyGenerator';

export class LeaderboardManager extends BaseManager {
  private config: LeaderboardConfig;

  constructor(client: RedisClient, config: Partial<LeaderboardConfig> = {}) {
    super(client, new KeyGenerator({ prefix: 'leaderboard' }));

    this.config = {
      maxEntries: 1000,
      scoreType: 'highest',
      updateStrategy: 'replace',
      timeWindow: { type: 'daily' },
      archiveOldEntries: true,
      ...config,
    };
  }

  /**
   * 更新分数
   */
  async updateScore(
    leaderboard: string,
    userId: string,
    score: number,
    metadata?: Record<string, any>
  ): Promise<void> {
    this.validateRequired({ leaderboard, userId });
    this.validateNumber(score, 'score');

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    const userDataKey = this.generateUserDataKey(leaderboard, userId);
    const startTime = this.now();

    try {
      // 获取当前分数用于比较
      const currentScore = await this.execute<string | null>('zscore', leaderboardKey, userId);
      const currentScoreNum = currentScore ? parseFloat(currentScore) : 0;

      let newScore = score;

      // 根据更新策略处理分数
      switch (this.config.updateStrategy) {
        case 'increment':
          newScore = currentScoreNum + score;
          break;
        case 'max':
          newScore = Math.max(currentScoreNum, score);
          break;
        case 'min':
          newScore = Math.min(currentScoreNum, score);
          break;
        case 'replace':
        default:
          newScore = score;
          break;
      }

      // 更新排行榜分数
      await this.execute('zadd', leaderboardKey, newScore, userId);

      // 保存用户元数据
      if (metadata) {
        const userData = {
          score: newScore,
          metadata,
          updatedAt: new Date(),
        };
        await this.setValue(userDataKey, userData);
      }

      // 限制排行榜条目数量
      if (this.config.maxEntries) {
        await this.trimLeaderboard(leaderboard);
      }

      this.logMetric('leaderboard.updateScore', this.now() - startTime, true, {
        leaderboard,
        userId,
        oldScore: currentScoreNum,
        newScore,
      });
    } catch (error) {
      this.logMetric('leaderboard.updateScore', this.now() - startTime, false, {
        leaderboard,
        userId,
        error,
      });
      throw this.formatError(error, 'Failed to update score');
    }
  }

  /**
   * 增加分数
   */
  async incrementScore(leaderboard: string, userId: string, by: number): Promise<number> {
    this.validateRequired({ leaderboard, userId });
    this.validateNumber(by, 'by');

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    const result = await this.execute<string>('zincrby', leaderboardKey, by, userId);

    // 限制排行榜条目数量
    if (this.config.maxEntries) {
      await this.trimLeaderboard(leaderboard);
    }

    return parseFloat(result);
  }

  /**
   * 获取用户排名
   */
  async getRank(leaderboard: string, userId: string): Promise<number | null> {
    this.validateRequired({ leaderboard, userId });

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    const command = this.config.scoreType === 'highest' ? 'zrevrank' : 'zrank';
    const rank = await this.execute<number | null>(command, leaderboardKey, userId);

    return rank !== null ? rank + 1 : null; // 转换为1基索引
  }

  /**
   * 获取用户分数
   */
  async getScore(leaderboard: string, userId: string): Promise<number | null> {
    this.validateRequired({ leaderboard, userId });

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    const score = await this.execute<string | null>('zscore', leaderboardKey, userId);

    return score ? parseFloat(score) : null;
  }

  /**
   * 获取前N名
   */
  async getTopN(
    leaderboard: string,
    n: number,
    includeMetadata: boolean = false
  ): Promise<LeaderboardEntry[]> {
    this.validateRequired({ leaderboard });
    this.validateNumber(n, 'n', 1);

    const query: LeaderboardQuery = {
      start: 0,
      end: n - 1,
      includeMetadata,
      includeScores: true,
    };

    const result = await this.query(leaderboard, query);
    return result.entries;
  }

  /**
   * 获取用户周围的排名
   */
  async getAroundUser(
    leaderboard: string,
    userId: string,
    range: number = 5
  ): Promise<LeaderboardEntry[]> {
    this.validateRequired({ leaderboard, userId });
    this.validateNumber(range, 'range', 1);

    const rank = await this.getRank(leaderboard, userId);

    if (rank === null) {
      return [];
    }

    const start = Math.max(0, rank - range - 1);
    const end = rank + range - 1;

    const query: LeaderboardQuery = {
      start,
      end,
      includeMetadata: true,
      includeScores: true,
    };

    const result = await this.query(leaderboard, query);
    return result.entries;
  }

  /**
   * 查询排行榜
   */
  async query(leaderboard: string, query: LeaderboardQuery): Promise<LeaderboardResult> {
    this.validateRequired({ leaderboard });

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    const isReverse = this.config.scoreType === 'highest';

    let start = query.start ?? 0;
    let end = query.end ?? -1;

    // 如果指定了around参数，计算范围
    if (query.around) {
      const rank = await this.getRank(leaderboard, query.around);
      if (rank !== null) {
        const range = query.range ?? 5;
        start = Math.max(0, rank - range - 1);
        end = rank + range - 1;
      }
    }

    // 获取排行榜数据
    const command = isReverse ? 'zrevrange' : 'zrange';
    const args = [leaderboardKey, start, end];

    if (query.includeScores) {
      args.push('WITHSCORES');
    }

    const rawResult = await this.execute<string[]>(command, ...args);
    const entries: LeaderboardEntry[] = [];

    // 解析结果
    if (query.includeScores) {
      for (let i = 0; i < rawResult.length; i += 2) {
        const userId = rawResult[i];
        const scoreStr = rawResult[i + 1];
        if (!userId || !scoreStr) continue;

        const score = parseFloat(scoreStr);
        const rank = start + i / 2 + 1;

        let metadata: Record<string, any> | undefined;
        if (query.includeMetadata) {
          const userDataKey = this.generateUserDataKey(leaderboard, userId);
          const userData = await this.getValue<any>(userDataKey);
          metadata = userData?.metadata;
        }

        entries.push({
          userId,
          score,
          rank,
          metadata,
          updatedAt: new Date(), // 简化实现
        });
      }
    } else {
      for (let i = 0; i < rawResult.length; i++) {
        const userId = rawResult[i];
        if (!userId) continue;

        const rank = start + i + 1;
        const score = (await this.getScore(leaderboard, userId)) ?? 0;

        entries.push({
          userId,
          score,
          rank,
          metadata: undefined,
          updatedAt: new Date(),
        });
      }
    }

    // 获取总数
    const totalCount = await this.execute<number>('zcard', leaderboardKey);

    // 获取用户排名和分数（如果指定了around）
    let userRank: number | undefined;
    let userScore: number | undefined;

    if (query.around) {
      userRank = (await this.getRank(leaderboard, query.around)) ?? undefined;
      userScore = (await this.getScore(leaderboard, query.around)) ?? undefined;
    }

    return {
      entries,
      totalCount,
      userRank,
      userScore,
      hasMore: end < totalCount - 1,
    };
  }

  /**
   * 获取时间窗口排行榜
   */
  async getTimeWindowLeaderboard(
    leaderboard: string,
    timeWindow: TimeWindow,
    date?: Date
  ): Promise<LeaderboardEntry[]> {
    this.validateRequired({ leaderboard });

    const windowKey = this.generateTimeWindowKey(leaderboard, timeWindow, date);
    const query: LeaderboardQuery = {
      start: 0,
      end: 99, // 前100名
      includeScores: true,
    };

    // 查询时间窗口排行榜
    const result = await this.query(windowKey, query);

    return result.entries;
  }

  /**
   * 获取日排行榜
   */
  async getDailyLeaderboard(leaderboard: string, date?: Date): Promise<LeaderboardEntry[]> {
    return this.getTimeWindowLeaderboard(leaderboard, { type: 'daily' }, date);
  }

  /**
   * 获取周排行榜
   */
  async getWeeklyLeaderboard(leaderboard: string, week?: Date): Promise<LeaderboardEntry[]> {
    return this.getTimeWindowLeaderboard(leaderboard, { type: 'weekly' }, week);
  }

  /**
   * 获取月排行榜
   */
  async getMonthlyLeaderboard(leaderboard: string, month?: Date): Promise<LeaderboardEntry[]> {
    return this.getTimeWindowLeaderboard(leaderboard, { type: 'monthly' }, month);
  }

  /**
   * 移除用户
   */
  async removeUser(leaderboard: string, userId: string): Promise<boolean> {
    this.validateRequired({ leaderboard, userId });

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    const userDataKey = this.generateUserDataKey(leaderboard, userId);

    const pipeline = this.createPipeline();
    pipeline.zrem(leaderboardKey, userId);
    pipeline.del(userDataKey);

    const results = await pipeline.exec();
    return (results[0][1] as number) > 0;
  }

  /**
   * 清空排行榜
   */
  async clear(leaderboard: string): Promise<void> {
    this.validateRequired({ leaderboard });

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    await this.deleteKeys(leaderboardKey);

    // 清理用户数据
    const pattern = this.generateUserDataKey(leaderboard, '*');
    const userDataKeys = await this.getKeys(pattern);
    if (userDataKeys.length > 0) {
      await this.deleteKeys(...userDataKeys);
    }
  }

  /**
   * 获取排行榜统计信息
   */
  async getStats(leaderboard: string): Promise<LeaderboardStats> {
    this.validateRequired({ leaderboard });

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    const totalEntries = await this.execute<number>('zcard', leaderboardKey);

    if (totalEntries === 0) {
      return {
        totalEntries: 0,
        averageScore: 0,
        medianScore: 0,
        topScore: 0,
        bottomScore: 0,
        lastUpdated: new Date(),
        updateFrequency: { perMinute: 0, perHour: 0, perDay: 0 },
      };
    }

    // 获取分数统计
    const isReverse = this.config.scoreType === 'highest';
    const topCommand = isReverse ? 'zrevrange' : 'zrange';
    const bottomCommand = isReverse ? 'zrange' : 'zrevrange';

    const [topResult, bottomResult] = await Promise.all([
      this.execute<string[]>(topCommand, leaderboardKey, 0, 0, 'WITHSCORES'),
      this.execute<string[]>(bottomCommand, leaderboardKey, 0, 0, 'WITHSCORES'),
    ]);

    const topScore = topResult.length >= 2 ? parseFloat(topResult[1]!) : 0;
    const bottomScore = bottomResult.length >= 2 ? parseFloat(bottomResult[1]!) : 0;

    // 计算中位数（简化实现）
    const medianIndex = Math.floor(totalEntries / 2);
    const medianResult = await this.execute<string[]>(
      topCommand,
      leaderboardKey,
      medianIndex,
      medianIndex,
      'WITHSCORES'
    );
    const medianScore = medianResult.length >= 2 ? parseFloat(medianResult[1]!) : 0;

    // 计算平均分（简化实现，实际可能需要遍历所有分数）
    const averageScore = (topScore + bottomScore) / 2;

    return {
      totalEntries,
      averageScore,
      medianScore,
      topScore,
      bottomScore,
      lastUpdated: new Date(),
      updateFrequency: { perMinute: 0, perHour: 0, perDay: 0 }, // 需要额外统计
    };
  }

  /**
   * 限制排行榜大小
   */
  private async trimLeaderboard(leaderboard: string): Promise<void> {
    if (!this.config.maxEntries) return;

    const leaderboardKey = this.keyGenerator.leaderboard(leaderboard);
    const totalEntries = await this.execute<number>('zcard', leaderboardKey);

    if (totalEntries > this.config.maxEntries) {
      const removeCount = totalEntries - this.config.maxEntries;
      const command = this.config.scoreType === 'highest' ? 'zremrangebyrank' : 'zrevrangebyrank';
      await this.execute(command, leaderboardKey, 0, removeCount - 1);
    }
  }

  /**
   * 生成用户数据键
   */
  private generateUserDataKey(leaderboard: string, userId: string): string {
    return this.keyGenerator.generate('userdata', leaderboard, userId);
  }

  /**
   * 生成时间窗口键
   */
  private generateTimeWindowKey(leaderboard: string, timeWindow: TimeWindow, date?: Date): string {
    const targetDate = date ?? new Date();
    let period: string;

    switch (timeWindow.type) {
      case 'daily':
        period = this.formatDate(targetDate, 'YYYY-MM-DD');
        break;
      case 'weekly': {
        // 简化实现：使用年-周数
        const weekNumber = this.getWeekNumber(targetDate);
        period = `${targetDate.getFullYear()}-W${weekNumber}`;
        break;
      }
      case 'monthly':
        period = this.formatDate(targetDate, 'YYYY-MM');
        break;
      case 'yearly':
        period = this.formatDate(targetDate, 'YYYY');
        break;
      case 'custom':
        period = `custom-${Math.floor(targetDate.getTime() / (timeWindow.duration ?? 3600) / 1000)}`;
        break;
      default:
        period = 'all-time';
    }

    return this.keyGenerator.leaderboard(leaderboard, period);
  }

  /**
   * 获取周数
   */
  private getWeekNumber(date: Date): number {
    const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
    const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000;
    return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
  }
}
